﻿/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

namespace SharpMixLib
{
    /// <summary>
    /// 
    /// </summary>
    public class Cpu : SingletonManager<Cpu>
    {
        /// <summary>
        /// 
        /// </summary>
        private int pc = 0;

        /// <summary>
        /// 
        /// </summary>
        private int origin = 0;

        /// <summary>
        /// 
        /// </summary>
        private Instruction[] program;

        /// <summary>
        /// 
        /// </summary>
        public int PC
        {
            get
            {
                return pc;
            }
            set
            {
                pc = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public int Origin
        {
            get
            {
                return origin;
            }
            set
            {
                origin = value;
            }
        }

        /// <summary>
        ///     Increases the program counter by one
        /// </summary>
        /// <returns>
        ///     True if there is a next command.
        ///     False otherwise.
        /// </returns>
        public bool NextCommand()
        {
            /// Halt detected
            if (pc == -1)
            {
                return false;
            }
            pc++;

            /// check if we've reached the end of our program
            if (pc >= program.Length)
                return false;
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="inst"></param>
        /// <returns></returns>
        public bool ExecuteCommand()
        {
            switch (program[pc].OpCode)
            {
                /// NOP
                case (int)OpCodes.NOP:
                    {
                        NextCommand();
                        break;
                    }
                /// 5: ADD
                /// 6: FADD
                case (int)OpCodes.ADD:
                    {
                        switch (program[pc].FieldCode)
                        {
                            /// ADD
                            case 5:
                                {
                                    RegisterManager.Instance.rA += Content(program[pc].Adress);
                                    break;
                                }
                            /// FADD
                            case 6:
                                {
                                    break;
                                }
                        }
                        break;
                    }
                /// 5: SUB
                /// 6: FSUB
                case (int)OpCodes.SUB:
                    {
                        switch (program[pc].FieldCode)
                        {
                            /// SUB
                            case 5:
                                {
                                    RegisterManager.Instance.rA -= Content(program[pc].Adress);
                                    break;
                                }
                            /// FSUB
                            case 6:
                                {
                                    break;
                                }
                        }
                        break;
                    }
                /// 0: NUM
                /// 1: CHAR
                /// 2: HLT
                case (int)OpCodes.NUM:
                    {
                        switch (program[pc].FieldCode)
                        {
                            /// HLT
                            case 2:
                                {
                                    Halt();
                                    break;
                                }
                        }
                        break;
                    }
                /// 0: SLA
                /// 1: SRA
                /// 2: SLAX
                /// 3: SRAX
                /// 4: SLC
                /// 5: SRC
                case (int)OpCodes.SLA:
                    {
                        switch (program[pc].FieldCode)
                        { 
                            /// SLA
                            case 0:
                                {
                                    RegisterManager.Instance.rA.ShiftLeft();
                                    break;
                                }
                            /// SRA
                            case 1:
                                {
                                    RegisterManager.Instance.rA.ShiftRight();
                                    break;
                                }
                        }
                        break;
                    }
                /// LDA
                /// Load CONTENTS(M)[l:r] int rA
                case (int)OpCodes.LDA:
                    {
                        RegisterManager.Instance.rA =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// LD1
                /// Load CONTENTS(M)[l:r] int rI1
                case (int)OpCodes.LD1:
                    {
                        RegisterManager.Instance.rI1 =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// LD2
                /// Load CONTENTS(M)[l:r] int rI2
                case (int)OpCodes.LD2:
                    {
                        RegisterManager.Instance.rI2 =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// LD3
                /// Load CONTENTS(M)[l:r] int rI3
                case (int)OpCodes.LD3:
                    {
                        RegisterManager.Instance.rI3 =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// LD4
                /// Load CONTENTS(M)[l:r] int rI4
                case (int)OpCodes.LD4:
                    {
                        RegisterManager.Instance.rI4 =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// LD5
                /// Load CONTENTS(M)[l:r] int rI5
                case (int)OpCodes.LD5:
                    {
                        RegisterManager.Instance.rI5 =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// LD6
                /// Load CONTENTS(M)[l:r] int rI6
                case (int)OpCodes.LD6:
                    {
                        RegisterManager.Instance.rI6 =
                            Content(program[pc].Adress).GetWordByField(program[pc].FieldCode);
                        break;
                    }
                /// STA
                /// Sets CONTENTS(M)[l:r] to the contents of rA
                case (int)OpCodes.STA:
                    {
                        Content(program[pc].Adress).SetWordByField(program[pc].FieldCode, 
                            RegisterManager.Instance.rA);
                        break;
                    }
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="adress"></param>
        /// <returns></returns>
        public Word Content(int adress)
        {
            return MemoryManager.Instance[adress];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cmd"></param>
        public void ParseWordCommandList(Word[] cmdlist)
        {
            program = new Instruction[cmdlist.Length];

            for (int i = 0; i < cmdlist.Length; ++i)
            {
                program[i] = new Instruction();
                program[i].ConvertFromWord(cmdlist[i]);
            }
        }

        /// <summary>
        ///     Sets the pc to -1 so that NextCommand returns false
        /// </summary>
        public void Halt()
        {
            pc = -1;
        }
    }
}
